package dipl.algorithm.math.utility;

import org.apfloat.Apfloat;
import org.apfloat.FixedPrecisionApfloatHelper;

/**
 * Class with functions and constants based on fixed precision numbers
 */
public class FPMath {

	//
	// CONSTRUCTOR METHODS
	//

	/**
	 * Constructs fixed-precision math object using given radix and given precision
	 * @param precision
	 * @param radix
	 */
	public FPMath( int precision ) {
		this.precision = precision;
		this.m = new FixedPrecisionApfloatHelper( precision );
		InitializeConstants();
	}

	//
	// AUXILLIARY METHODS
	//

	/**
	 * Initializes mathematical constants provided by object.
	 */
	protected void InitializeConstants() {
		_0_5					= ValueOf( 0.5 );
		_5						= ValueOf( 5 );
		_4						= ValueOf( 4 );
		_3					  = ValueOf( 3 );
		_2						= ValueOf( 2 );
		_m1						= ValueOf( -1 );
		_1						= ValueOf( 1 );
		_0						= ValueOf( 0 );
		PI						= m.pi( ApfloatUtils.radix );
		_2PI					= m.multiply( PI, _2 );
		PI_DIV2				= m.divide( PI, _2 );
		PI_DIV180			= m.divide( PI, ValueOf( 180.0 ) );
		INV_PI_DIV180 = m.divide( _1, PI_DIV180 );
	}
	
	//
	// STATIC MEMBERS
	//

	//
	// MEMBERS
	//

	/** global fixed precision */
	private int precision;

	/** Object performing actual strict fixed precision calculations */
	public FixedPrecisionApfloatHelper m;

	/* immutable 0.5 */
	public Apfloat _0_5;

	/* immutable 5.0 */
	public Apfloat _5;

	/* immutable 4.0 */
	public Apfloat _4;

	/* immutable 3.0 */
	public Apfloat _3;

	/* immutable 2.0 */
	public Apfloat _2;

	/** immutable -1.0 */
	public Apfloat _m1;

	/** immutable 1.0 */
	public Apfloat _1;

	/** immutable 0.0 */
	public Apfloat _0;

	/* immutable pi */
	public Apfloat PI;

	/* immutable 2.0*pi */
	public Apfloat _2PI;

	/* immutable pi/2.0 */
	public Apfloat PI_DIV2;

	/* immutable pi/180.0 */
	public Apfloat PI_DIV180;

	/* immutable 1.0/(pi/180.0) */
	public Apfloat INV_PI_DIV180;

	//
	// PROPERTIES
	//

	/**
	 * @return precision of numbers and calculations used
	 */
	public int Precision() {
		return precision;
	}

	/**
	 * @return Calculation radix (standard is 2)
	 */
	public int Radix() {
		return precision;
	}

	/**
	 * Sets precision used by all
	 */
	public void SetPrecision( int precision ) {
		this.precision = precision;
		// reinitialize mathematic constants and fixed precision helper
		m = new FixedPrecisionApfloatHelper( precision );
		InitializeConstants();
	}

	//
	// PUBIC METHODS
	//

	/**
	 * Returns copy of given apfloat
	 * @param x
	 * @return
	 */
	public Apfloat CopyOf( Apfloat x ) {
		return x.add( Apfloat.ZERO );
	}

	/**
	 * Creates apfloat with current precision, radix and value of given double
	 * @param x
	 * @return
	 */
	public Apfloat ValueOf( double x ) {
		return new Apfloat( x, precision, ApfloatUtils.radix );
	}

	/**
	 * Creates apfloat with current precision, radix and value of given long
	 * @param x
	 * @return
	 */
	public Apfloat ValueOf( long x ) {
		return new Apfloat( x, precision, ApfloatUtils.radix );
	}
}
